package com.summer.data.apt;

import com.summer.data.annotation.CopyColumn;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Names;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.summer.data.apt.Constant.COLUMN_DATA_MAP;

//@SupportedAnnotationTypes("org.summer.data.meta.TableName")
@SupportedAnnotationTypes("*")
//@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class CopyColumnProcessor extends AbstractProcessor {

    private Messager messager;
    /**
     * JavacTrees提供了待处理的抽象语法树
     * TreeMaker中了一些操作抽象语法树节点的方法
     * Names提供了创建标识符的方法
     */
    private JavacTrees trees;
    private TreeMaker treeMaker;
    private Names names;
    private JavacElements elementUtils;

    TreeHelper treeHelper;

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_8;
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);

//        System.out.println(String.format("%s init.", CopyColumnProcessor.class));
        this.messager = processingEnv.getMessager();
        this.trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
        this.elementUtils = (JavacElements)processingEnv.getElementUtils();
        this.treeHelper = new TreeHelper(messager, trees, treeMaker, names);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

        Set<? extends Element> annotatedWithTableNameSet = roundEnv.getElementsAnnotatedWith(CopyColumn.class);
        for (Element copyFieldElement : annotatedWithTableNameSet) {
//            System.out.println(String.format(" 类 %s 发现注解 %s.", copyFieldElement, CopyColumn.class));

            JCTree.JCClassDecl targetClassDecl = (JCTree.JCClassDecl) trees.getTree(copyFieldElement);

            // 获取所有 CopyField 注解
            List<? extends AnnotationMirror> annotationMirrors = copyFieldElement.getAnnotationMirrors();
            // 遍历所有 CopyField 注解
            annotationMirrors.forEach(copyFieldMirror -> {
                Map<String, Object> attributes = getAttributes(copyFieldMirror);
                for (Map.Entry<String, Object> entry : attributes.entrySet()) {
                    // key == "value"
                    String key = entry.getKey();
                    if ("value".equals(key)) {
                        Object classTypeObject = entry.getValue();
                        if (classTypeObject instanceof Type.ClassType) {
                            Type.ClassType classType = (Type.ClassType) classTypeObject;
                            // 获取类的定义
                            JCTree.JCClassDecl tableClassDef = (JCTree.JCClassDecl) elementUtils.getTree(classType.asElement());

//                            System.out.println(String.format("copy column:  %s", tableClassDef));
//                            messager.printMessage();
                            copyColumns(targetClassDecl, tableClassDef);
                        }
                    }
                }
            });
        }

        return false;
    }

    private Map<String, Object> getAttributes(AnnotationMirror annotation) {
        Map<String, Object> attributes = new LinkedHashMap<>();
        for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotation.getElementValues().entrySet()) {
            Symbol.MethodSymbol key = (Symbol.MethodSymbol) entry.getKey();
            Attribute.Class value = (Attribute.Class)entry.getValue();
            Type value1 = value.getValue();
            attributes.put(key.getQualifiedName().toString(), value1);
        }
        return attributes;
    }

    private boolean hasFlag(JCTree.JCVariableDecl varDecl, Modifier modifier) {
        Set<Modifier> flags = varDecl.getModifiers().getFlags();
        for (Modifier flag : flags) {
            if (flag == modifier) {
                return true;
            }
        }
        return false;
    }

    /**
     * 遍历 XxxTable 类的 Column 列，逐个拷贝
     * @param targetClassDecl   目标类定义
     * @param tableClassDef     表类定义
     */
    private void copyColumns(JCTree.JCClassDecl targetClassDecl, JCTree.JCClassDecl tableClassDef) {
        for (JCTree memberDecl : tableClassDef.defs) {
            // 只拷贝变量
            if (memberDecl instanceof  JCTree.JCVariableDecl) {
                JCTree.JCVariableDecl varDecl = (JCTree.JCVariableDecl) memberDecl;

                // 跳过 ShareTable 变量
                String shareTableVarName = ClassUtil.getShareTableName(tableClassDef);
                if (varDecl.getName().toString().equals(shareTableVarName)) {
                    continue;
                }
                // 跳过 static 变量
                if (hasFlag(varDecl, Modifier.STATIC)) {
                    continue;
                }
                //
                if (varDecl.vartype.type == null) {
                    continue;
                }
                // 只有 XxxColumn 类型才需要生成
                Type type = varDecl.vartype.type;
                if (COLUMN_DATA_MAP.containsKey(type.toString())) {
                    makeVarDecl(targetClassDecl, varDecl);
                    makeSetterMethodDecl(targetClassDecl, varDecl);
                    makeGetterMethodDecl(targetClassDecl, varDecl);
                }
            }
        }
    }

    private void makeVarDecl(JCTree.JCClassDecl targetClassDecl, JCTree.JCVariableDecl varDecl) {
        JCTree.JCVariableDecl newVarDecl = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PRIVATE),             // 访问标识
                varDecl.name,                                   // 变量名
                treeHelper.column2DataType(varDecl),            // 变量类型
                null                                            // 初始化表达式
        );
        targetClassDecl.defs = targetClassDecl.defs.append(newVarDecl);
    }
    /**
     * 生成 setter 方法
     * @param varDecl
     * @return
     */
    private void makeSetterMethodDecl(JCTree.JCClassDecl targetClassDecl, JCTree.JCVariableDecl varDecl) {

        /**
         * public void setName(String name) {
         *     this.name = name;
         * }
         * private
         */
        String setterMethodName = ClassUtil.toSetterName(varDecl);
        if (ClassUtil.methodExists(setterMethodName, targetClassDecl)) {
            return;
        }

        // this.xxx
        JCTree.JCFieldAccess thisX = treeMaker.Select(treeMaker.Ident(names.fromString("this")), varDecl.getName());
        // this.xxx = xxx
        JCTree.JCAssign assign = treeMaker.Assign(thisX, treeMaker.Ident(varDecl.getName()));

        // this.xxx = xxx;
        com.sun.tools.javac.util.List<JCTree.JCStatement> statements = com.sun.tools.javac.util.List.of(treeMaker.Exec(assign));

        JCTree.JCBlock body = treeMaker.Block(0, statements);

        JCTree.JCExpression methodReturnType = treeMaker.Type(new Type.JCVoidType());

        // 生成方法参数之前，指明当前语法节点在语法树中的位置，避免出现异常 java.lang.AssertionError: Value of x -1
        treeMaker.pos = varDecl.pos;
        JCTree.JCVariableDecl param = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PARAMETER),
                varDecl.name,
                treeHelper.column2DataType(varDecl),
                null
        );

        JCTree.JCMethodDecl methodDecl = treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC),          // mods：访问标志
                names.fromString(setterMethodName),         // name：方法名
                methodReturnType,                           // restype：返回类型
                com.sun.tools.javac.util.List.nil(),                                 // typarams：泛型参数列表
                com.sun.tools.javac.util.List.of(param),                                 // params：参数列表
                com.sun.tools.javac.util.List.nil(),                                 // thrown：异常声明列表
                body,                                       //方法体
                null                                        // init
        );
        targetClassDecl.defs = targetClassDecl.defs.append(methodDecl);
    }

    /**
     * 生成 getter 方法
     * @param varDecl
     * @return
     */
    private void makeGetterMethodDecl(JCTree.JCClassDecl targetClassDecl, JCTree.JCVariableDecl varDecl) {

        String getterMethodName = ClassUtil.toGetterName(varDecl);

        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();

        statements.append(
                treeMaker.Return(
                        treeMaker.Select(
                                treeMaker.Ident(names.fromString("this")), varDecl.getName()
                        )
                )
        );

        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());

        JCTree.JCMethodDecl methodDecl = treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC),          // mods：访问标志
                names.fromString(getterMethodName),                   // name：方法名
                treeHelper.column2DataType(varDecl),                     // restype：返回类型
                com.sun.tools.javac.util.List.nil(),                                 // typarams：泛型参数列表
                com.sun.tools.javac.util.List.nil(),                                 // params：参数列表
                com.sun.tools.javac.util.List.nil(),                                 // thrown：异常声明列表
                body,                                       //方法体
                null                                        // init
        );
        targetClassDecl.defs = targetClassDecl.defs.append(methodDecl);
    }

}


